<?php

namespace app\common\service;

use app\admin\service\BaseService;
use app\common\model\CronLogModel;
use app\common\model\CronModel;
use Exception;

class CronService extends BaseService
{

    /**
     * 获取计划任务列表
     *
     * @Author tanruizheng
     * @DateTime 2021-07-12
     * @param array $where
     * @return void
     */
    public static function getCronList($where = [], $page = 1, $limit = 15){
        $list = CronModel::where($where)->page($page,$limit)->select()->toArray();
        foreach ($list as &$item) {
            $item['modified_time'] = $item['modified_time'] ? date('Y-m-d H:i:s',$item['modified_time']) : '';
            $item['next_time'] = $item['next_time'] ? date('Y-m-d H:i:s',$item['next_time']) : '';
            $loopDaytime = $item['loop_daytime'];
            list($day,$hour,$minute) = explode('-',$loopDaytime);
            switch ($item['loop_type']) {
                case 'month':
                    $item['loop_type_text'] = '每月';
                    break;
                case 'week':
                    $item['loop_type_text'] = '每周';
                    break;
                case 'day':
                    $item['loop_type_text'] = '每天';
                    break;
                case 'hour':
                    $item['loop_type_text'] = '每小时';
                    break;
                case 'now':
                    $nowTime = $day + $hour + $minute;
                    $nowType = $day ? '天' : ($hour ? '小时' : '分钟');
                    $item['loop_type_text'] = '每隔'.$nowTime.$nowType;
                    break;
            }
        }
        $count = CronModel::where($where)->count();
        return self::createList($list,$count);
    }

    /**
     * 添加或者编辑计划任务
     *
     * @Author tanruizheng
     * @DateTime 2021-07-09
     * @param [type] $params
     * @return void
     */
    public static function addOrEditData($params){
        try {
            if(empty($params['subject'])){
                return self::createFail('计划任务名称不能为空');
            }
            if(empty($params['cron_file'])){
                return self::createFail('计划任务执行文件不能为空');
            }
            if(empty($params['loop_data'])){
                return self::createFail('执行时间不能为空');
            }
            //获取循环类型和下次执行时间
            $loopData = self::getLoopData($params['loop_type'],$params['loop_data']);
            $data = [
                'subject'       => $params['subject'],
                'loop_type'     => $params['loop_type'],
                'loop_daytime'  => $loopData['loopDaytime'],
                'cron_file'     => $params['cron_file'],
                'isopen'        => $params['isopen'],
                'next_time'     => $loopData['nextTime'],
            ];
            if(isset($params['cron_id']) && $params['cron_id']){
                if(CronModel::where('subject',$params['subject'])->where('cron_id','<>',$params['cron_id'])->count()){
                    return self::createFail('计划任务不能重复!');
                }
                $res = CronModel::where('cron_id',$params['cron_id'])->save($data);
            }else{
                $data['created_time'] = time();
                if(CronModel::where('subject',$params['subject'])->count()){
                    return self::createFail('计划任务不能重复!');
                }
                $res = CronModel::create($data);
            }
            if($res !== false){
                return self::createSuccess('保存成功');
            }
            return self::createFail('保存失败');
        } catch (Exception $e) {
            // 验证失败 输出错误信息
            return self::createFail($e->getMessage());
        }
    }

    /**
     * 获取计划任务详情
     *
     * @Author tanruizheng
     * @DateTime 2021-07-13
     * @param [type] $roleId
     * @return void
     */
    public static function getCronDetail($cronId){
        $cronModel = CronModel::where('cron_id',$cronId)->findOrEmpty();
        $loopDaytime = $cronModel->loop_daytime;
        list($day,$hour,$minute) = explode('-',$loopDaytime);
        switch ($cronModel->loop_type) {
            case 'now':
                $cronModel->now_time = $day + $hour + $minute;
                $cronModel->now_type = $day ? 'day' : ($hour ? 'hour' : 'minute');
                break;
            case 'month':
                $cronModel->month_day = $day;
                $cronModel->month_hour = $hour;
                break;
            case 'week':
                $cronModel->week_day = $day;
                $cronModel->week_hour = $hour;
                break;
            case 'day':
                $cronModel->day_hour = $hour;
                break;
            case 'hour':
                $cronModel->hour_minute = $minute;
                break;
        }
        return $cronModel;
    }

    /**
     * 计划任务删除
     *
     * @Author tanruizheng
     * @DateTime 2021-07-20
     * @param [type] $cronId
     * @return void
     */
    public static function cronDelete($cronId){
        if(!$cronId){
            return self::createFail('参数错误!');
        }
        $result = CronModel::where('cron_id','IN',$cronId)->delete();
        if($result !== false){
            return self::createSuccess('删除成功');
        }
        return self::createSuccess('删除失败');
    }

    /**
     * 获取计划任务循环类型和下次执行时间
     *
     * @Author tanruizheng
     * @DateTime 2021-07-20
     * @param [type] $loopType
     * @param [type] $loopData
     * @return void
     */
    public static function getLoopData($loopType,$loopData){
        switch ($loopType) {
            case 'now'://每隔(天/小时、分)
                $nowTime = $loopData['now_time'];
                $nowType = $loopData['now_type'];
                $minute = $nowType == 'minute' ? $nowTime : 0;
                $hour = $nowType == 'hour' ? $nowTime : 0;
                $day = $nowType == 'day' ? $nowTime : 0;
                //循环类型时间（日-时-分）
                $loopDaytime = $day.'-'.$hour.'-'.$minute;
                //下次执行时间
                $nextTime = self::getNextTime($loopType,$day,$hour,$minute);
                break;
            case 'month'://每月
                $monthDay = $loopData['month_day'];
                $monthHour = $loopData['month_hour'];
                //循环类型时间（日-时-分）
                $loopDaytime = $monthDay.'-'.$monthHour.'-0';
                //下次执行时间
                $nextTime = self::getNextTime($loopType,$monthDay,$monthDay);
                break;
            case 'week'://每周
                $weekDay = $loopData['week_day'];
                $weekHour = $loopData['week_hour'];
                //循环类型时间（日-时-分）
                $loopDaytime = $weekDay.'-'.$weekHour.'-0';
                //下次执行时间
                $nextTime = self::getNextTime($loopType,$weekDay,$weekHour);
                break;
            case 'day'://每天
                $weekHour = $loopData['day_hour'];
                //循环类型时间（日-时-分）
                $loopDaytime = '0-'.$weekHour.'-0';
                //下次执行时间
                $nextTime = self::getNextTime($loopType,0,$weekHour);
                break;
            case 'hour'://每小时
                $hourMinute = $loopData['hour_minute'];
                //循环类型时间（日-时-分）
                $loopDaytime = '0-0-'.$hourMinute;
                //下次执行时间
                $nextTime = self::getNextTime($loopType,0,0,$hourMinute);
                break;
        }
        return [
            'loopDaytime'   => $loopDaytime,
            'nextTime'      => $nextTime,
        ];
    }

    /**
     * 获取下次执行时间
     *
     * @Author tanruizheng
     * @DateTime 2021-07-20
     * @param [type] $loopType
     * @param [type] $day
     * @param [type] $hour
     * @param [type] $minute
     * @return void
     */
    public static function getNextTime($loopType, $day, $hour, $minute = 0){
        $nextTime = '';
        $time = time();//当前时间
        $_year = date('Y',$time);//年
        $_month = date('n',$time);//月
        $_week = date('w',$time);//周
        $_day = date('j',$time);//日
        $_hour = date('G',$time);//小时
        $_minute = date('i',$time);//分钟
        $nextTime =  mktime($_hour,0,0,$_month,$_day,$_year);
        switch ($loopType) {
            case 'now'://每隔
                $nextTime = mktime($_hour,$_minute,0,$_month,$_day,$_year);
                $times = $day * 24 * 60;
                $times += $hour * 60;
                $times += $minute;
                $times = $times * 60;
                $nextTime += $times;
                break;
            case 'month'://每月
                $isLeapYear = date('L',$time);//是否闰年
                $monthDay = self::getMonthDays($_month,$isLeapYear);
                if($_hour < $hour){
                    $nextTime += ($hour - $_hour) * 3600;
                }else{
                    $nextTime += -($_hour - $hour) * 3600;
                }
                if($_day == $day && $hour < $_hour){
                    $nextTime += $monthDay * 86400;
                }else{
                   if($day < $_day){
                        $nextTime += ($monthDay-$_day+$day) * 86400;
                   }else{
                        $nextTime += ($day-$_day) * 86400;
                   }
                }
                break;
            case 'week'://每周
                if($_hour < $hour){
                    $nextTime += ($hour - $_hour) * 3600;
                }else{
                    $nextTime += -($_hour - $hour) * 3600;
                }
                if($_week == $day && $hour < $_hour){
                    $nextTime += 7 * 24 * 3600;
                }else{
                   if($day < $_week){
                        $nextTime += (7-$_week+$day) * 86400;
                   }else{
                        $nextTime += ($day-$_week) * 86400;
                   }
                }
                break;
            case 'day'://每天
                if($hour > $_hour){
                    $nextTime += ($hour-$_hour) * 3600;
                }else{
                    $nextTime += -($_hour - $hour) * 3600;
                    $nextTime += 24 * 60 * 60;
                }
                break;
            case 'hour'://每小时
                if($minute > $_minute){
                    $nextTime += $minute * 60;
                }else{
                    $nextTime += (60 + $minute) * 60;
                }
                break;
        }
        return $nextTime;
    }

    /**
     * 执行计划任务
     *
     * @Author tanruizheng
     * @DateTime 2021-07-21
     * @param CronModel $cronModel
     * @return void
     */
    public static function runAction(CronModel $cronModel){
        try {
            $startTime = time();//开始时间
            $cronFile = $cronModel->cron_file;
            $class = new $cronFile();
            $res = $class->run($cronModel->cron_id);
            $endTime = time();//结束时间
            $useTime = $endTime - $startTime;
            if($res['status']){
                $result = 1;//执行正常
            }else{
                $result = 2;//执行异常
            }
            $resultMsg = $res['msg'];//执行返回结果提示
        } catch (\Exception $e) {
            $result = 2;
            $resultMsg = $e->getMessage();
        }
        $cronLog = new CronLogModel();
        $cronLog->cron_id = $cronModel->cron_id;
        $cronLog->start_time = $startTime;
        $cronLog->end_time = $endTime;
        $cronLog->result = $result;
        $cronLog->use_time = $useTime;
        $cronLog->result_msg = $resultMsg;
        $cronLog->save();
        if($result == 1){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 获取天数
     *
     * @Author tanruizheng
     * @DateTime 2021-07-22
     * @return void
     */
    public static function getDaysOptions(){
        $days = [];
        for ($i=1; $i <=32 ; $i++) { 
            $arr['value'] = $i < 32 ? $i : 99;
            $arr['text'] = $i < 32 ? $i.'号' : '最后一天';
            $days[] = $arr;
        }
        return $days;
    }

    /**
     * 获取小时
     *
     * @Author tanruizheng
     * @DateTime 2021-07-22
     * @return void
     */
    public static function getHoursOptions(){
        $hours = [];
        for ($i=0; $i < 24 ; $i++) { 
            $arr['value'] = $i;
            $arr['text'] = $i.'时';
            $hours[] = $arr;
        }
        return $hours;
    }

    public static function getMonthDays($month,$isLeapYear){
        if(in_array($month,[1,3,5,7,8,10,12])){
            $day = 31;
        }elseif(in_array($month,[4,6,9,11])){
            $day = 30;
        }else{
            if($isLeapYear){
                $day = 29;
            }else{
                $day = 28;
            }
        }
        return $day;
    }
}